﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace DarwinIsDead {
    /// <summary>
    /// Base class for all projectiles in Darwin is Dead.
    /// </summary>
    /// <remarks>
    /// A <see cref="Bullet"/> cause damage to both Player and Enemies,
    /// depending on its source.  This class provides the basic
    /// characteristics common to all projectiles in Darwin is Dead.
    /// 
    /// Extend this class to create your own bullet type.
    /// </remarks>
    public abstract class Bullet : Entity {
        private bool active;

        /// <summary>
        /// Delegate for notifying damage done to firing <see cref="Entity"/>.
        /// </summary>
        /// <remarks>
        /// Delegates are fancy function pointers in C#.  Look them up.  When
        /// you create a trait that fires bullets, make sure to call
        /// NotifyDamage to let the firing Entity know the bullet hit.
        /// </remarks>
        /// <param name="damage">How much damage the bullet dealt</param>
        public delegate void DamageNotifier(int damage);
        public DamageNotifier NotifyDamage { get; set; }

        /// <summary>
        /// Constructs a bullet
        /// </summary>
        /// <remarks>
        /// Creates a bullet with the specified dimensions and damage output.
        /// A bullet's position is undefined until it is fired.
        /// </remarks>
        /// <param name="world"></param>
        /// <param name="w">hit box width</param>
        /// <param name="h">hit box height</param>
        /// <param name="friendly">friend or foe tag.  True if fired by player,
        /// false otherwise.</param>
        /// <param name="damage">damage output.  Negative for healing</param>
        public Bullet(World world, float w, float h, bool friendly,
            int damage)
            : base(world, 0f, 0f, w, h, false) {
            Friendly = friendly;
            Damage = damage;
        }

        /// <summary>
        /// Constructs a bullet
        /// </summary>
        /// <remarks>
        /// Creates a bullet with the given rectangle's dimensions and provided
        /// damage output.  <c>rect</c>'s x and y coordinates are ignored.
        /// A bullet's position is undefined until it is fired.
        /// </remarks>
        /// <param name="world"></param>
        /// <param name="rect">hit box dimensions</param>
        /// <param name="friendly">friend or foe tag.  True if fired by player,
        /// false otherwise.</param>
        /// <param name="damage">damage output.  Negative for healing</param>
        public Bullet(World world, Rectangle rect, bool friendly, int damage)
            : base(world, new Rectangle(0, 0, rect.Width, rect.Height), false) {
            Friendly = friendly;
            Damage = damage;
        }

        /// <summary>
        /// Constructs a bullet
        /// </summary>
        /// <remarks>
        /// Creates a bullet with the specified dimensions and damage output.
        /// A bullet's position is undefined until it is fired.
        /// </remarks>
        /// <param name="world"></param>
        /// <param name="dim">hit box dimensions</param>
        /// <param name="friendly">friend or foe tag.  True if fired by player,
        /// false otherwise.</param>
        /// <param name="damage">damage output.  Negative for healing</param>
        public Bullet(World world, Vector2 dim, bool friendly, int damage)
            : base(world, Vector2.Zero, dim, false) {
            Friendly = friendly;
            Damage = damage;
        }

        /// <summary>
        /// Whether or not a bullet is live.  An active bullet updates and
        /// renders, whereas an inactive one doesn't.
        /// </summary>
        public bool Active {
            get { return active; }
            set {
                active = value;
                Solid = value;
            }
        }

        /// <summary>
        /// Friend or foe.  True if fired by the player, false if fired
        /// by an enemy.
        /// </summary>
        public bool Friendly { get; set; }

        /// <summary>
        /// Direction the marksman is facing when this <see cref="Bullet"/>
        /// is fired, in radians.
        /// </summary>
        public float FireAngle { get; set; }

        /// <summary>
        /// The amount of damage this <see cref="Bullet"/> does on impact.
        /// Can be negative for healing.
        /// </summary>
        public int Damage { get; set; }

        /// <summary>
        /// Actives this <see cref="Bullet"/>
        /// </summary>
        /// <remarks>
        /// Enables this bullet.
        /// 
        /// Use this method to play a firing sound unique to this bullet,
        /// or if needed, to set the Solid property.  That last bit is VERY
        /// important in order for this to hit anything
        /// </remarks>
        public virtual void Fire() {
            Active = true;
            Solid = true;
        }

        /// <summary>
        /// Activates this <see cref="Bullet"/>.
        /// </summary>
        /// <remarks> Places this Bullet at the given position and
        /// angle then enables it to begin updating its own logic.
        /// </remarks>
        /// <param name="position"></param>
        /// <param name="angle"></param>
        public virtual void Fire(Vector2 position, float angle) {
            Position = position;
            FireAngle = angle;
            Active = true;
            Solid = true;
        }

        /// <summary>
        /// Activates this <see cref="Bullet"/>.
        /// </summary>
        /// <remarks> Places this Bullet at the given position and
        /// angle then enables it to begin updating its own logic.
        /// </remarks>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="angle"></param>
        public void Fire(float x, float y, float angle) {
            SetPosition(x, y);
            FireAngle = angle;
            Active = true;
            Solid = true;
        }

        /// <summary>
        /// Logic update.
        /// </summary>
        /// <remarks>
        /// <see cref="Bullet"/>s only update if active.  To define logic
        /// update behavior, override <see cref="InternalUpdate(GameTime)"/>.
        /// 
        /// The body of this method is essentially this:
        /// <code>
        ///     if (active) {
        ///         InternalUpdate(gameTime);
        ///     }
        /// </code>
        /// </remarks>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime) {
            if (Active) {
                InternalUpdate(gameTime);
            }
        }

        /// <summary>
        /// Rendering update.
        /// </summary>
        /// <remarks>
        /// <see cref="Bullet"/>s only render if active.  To define drawing
        /// update behavior, override <see cref="InternalDraw(SpriteBatch)"/>.
        /// 
        /// The body of this method is essentially this:
        /// <code>
        ///     if (active) {
        ///         InternalDraw(spriteBatch);
        ///     }
        /// </code>
        /// </remarks>
        /// <param name="spriteBatch"></param>
        public override void Draw(SpriteBatch spriteBatch) {
            if (Active) {
                InternalDraw(spriteBatch);
            }
        }

        /// <summary>
        /// Render UI update
        /// </summary>
        /// <remarks>
        /// <see cref="Bullet"/>s only render if active.  To define drawing
        /// update behavior, override <see cref="InternalDrawUI(SpriteBatch)"/>.
        /// 
        /// The body of this method is essentially this:
        /// <code>
        ///     if (active) {
        ///         InternalDrawUI(spriteBatch);
        ///     }
        /// </code>
        /// </remarks>
        /// <param name="spriteBatch"></param>
        public override void DrawUI(SpriteBatch spriteBatch) {
            if (Active) {
                InternalDrawUI(spriteBatch);
            }
        }

        /// <summary>
        /// Internal logic update.  Override this to define logic behavior.
        /// </summary>
        /// <param name="gameTime"></param>
        protected virtual void InternalUpdate(GameTime gameTime) { }

        /// <summary>
        /// Render update.  Override this to define drawing behavior.
        /// </summary>
        /// <param name="spriteBatch"><see cref="SpriteBatch"/> to draw to</param>
        protected virtual void InternalDraw(SpriteBatch spriteBatch) { }

        /// <summary>
        /// Render UI update.  Override this if you want to draw something
        /// to the screen.
        /// </summary>
        /// <param name="spriteBatch"><see cref="SpriteBatch"/> to draw to</param>
        protected virtual void InternalDrawUI(SpriteBatch spriteBatch) { }
    }
}
